﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using StockFinder.Model;
using StockFinder.DataAccess;

namespace StockFinder.Services.PriceDatabaseAnalysers
{
    public class WithinSpecifiedRangeOfAllTimeHighPriceDatabaseAnalyser
    {
        private static readonly ILog _Log = LogManager.GetLogger(typeof(WithinSpecifiedRangeOfAllTimeHighPriceDatabaseAnalyser));

        private List<PriceDatabaseAnalyserResult> _Results;

        public WithinSpecifiedRangeOfAllTimeHighPriceDatabaseAnalyser()
        {
            _Results = new List<PriceDatabaseAnalyserResult>();
        }

        private const int RANGE = 15;

        public PriceDatabaseAnalyserResult AnalysePrices(List<DailyPrice> prices)
        {
            var result = new PriceDatabaseAnalyserResult();

            if (prices != null && prices.Count > 0)
            {
                //order the prices
                var orderedPrices = prices.OrderByDescending(p => p.PriceDate);
                
                //get last price
                DailyPrice lastPrice = orderedPrices.ElementAt(0);

                //make sure this has a price history, and not IPO day
                if (prices.Count() > 1)
                {
                    try
                    {
                        /* is the previouse close higher than any previous high */

                        //get all time high before today
                        DailyPrice allTimeHigh = orderedPrices.Skip(1).AllTimeHighestPriceByAdjustedHigh();

                        //is the last price close higher than the previous all time high?
                        if (lastPrice.AdjustedClose <= allTimeHigh.AdjustedHigh)
                        {
                            //4. get all prices since all time high
                            var baseSinceAllTimeHigh = orderedPrices.Where(i => i.PriceDate >= allTimeHigh.PriceDate);

                            //5. make sure there are no 0 prices
                            if (!baseSinceAllTimeHigh.Any(i => i.AdjustedLow == 0))
                            {
                                //6. get the low of the base
                                var baseLow = baseSinceAllTimeHigh.Min(i => i.AdjustedLow);

                                //7. the daily price for that low
                                var baseLowPrice = baseSinceAllTimeHigh.First(i => i.AdjustedLow == baseLow);

                                var timeInBase = lastPrice.PriceDate - allTimeHigh.PriceDate;

                                var daysInBase = timeInBase.Days;

                                //determine the base depth
                                var baseDepth = (allTimeHigh.AdjustedHigh - baseLow) / allTimeHigh.AdjustedHigh * 100;

                                //% from all time high
                                var depthToAllTimeHigh = (allTimeHigh.AdjustedHigh - lastPrice.AdjustedClose) / allTimeHigh.AdjustedHigh * 100;

                                var sma50 = orderedPrices.GetSimpleMovingAverage(50);

                                if (sma50 > 0)
                                {
                                    result.PercentFromSMA50 = lastPrice.AdjustedClose - sma50;

                                    result.PercentFromSMA50 = (result.PercentFromSMA50 / sma50) * 100;
                                }

                                //set diagnostics
                                if (depthToAllTimeHigh < RANGE)
                                {
                                    result.Succeeded = true;
                                    result.DepthFromHigh = depthToAllTimeHigh;
                                    result.BaseLength = baseSinceAllTimeHigh.Count();
                                    result.BaseLowDate = baseLowPrice.PriceDate;
                                    result.BaseLowPrice = baseLow;
                                    result.DaysOfPrices = orderedPrices.Count();
                                    result.PreviousHighPrice = allTimeHigh.AdjustedHigh;
                                    result.PreviousHighDate = allTimeHigh.PriceDate;
                                    result.BaseDepth = baseDepth;
                                    result.BaseLength = daysInBase;
                                }
                            }
                            else
                            {
                                _Log.Warn("Contains a 0 price, skipping");
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        _Log.Error(exception);
                    }
                }
            }
            else
            {
                _Log.Warn("No prices");
            }

            return result;
        }
    }
}
